package com.cloudkinto.service.order.claim.impl;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.bo.claim.ClaimAddBo;
import com.cloudkinto.bo.claim.ClaimOperate;
import com.cloudkinto.bo.claim.ClaimQueryBo;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.ExcelExportTitleStyle;
import com.cloudkinto.common.RedisKeyConstants;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.ClaimDao;
import com.cloudkinto.dao.ProductDao;
import com.cloudkinto.dao.StorageLocationDao;
import com.cloudkinto.dto.order.claim.ClaimOmsDto;
import com.cloudkinto.dto.order.claim.ClaimWmsDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.claim.ClaimStatusEnum;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.order.claim.ClaimProductService;
import com.cloudkinto.service.order.claim.ClaimService;
import com.cloudkinto.service.order.returns.OrderReturnService;
import com.cloudkinto.service.warehouse.WarehouseService;
import com.cloudkinto.vo.claim.ClaimDetailVo;
import com.cloudkinto.vo.claim.ClaimProductVo;
import com.cloudkinto.vo.claim.ClaimVo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 入库认领主表 service实现类
 * </p>
 *
 * @author long
 * @since 2024-11-18
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class ClaimServiceImpl extends ServiceImpl<ClaimDao, ClaimDo> implements ClaimService {

    private final StorageLocationDao storageLocationDao;
    private final CompanyService companyService;
    private final RedisTemplate redisTemplate;
    private final ClaimProductService claimProductService;
    private final FileService fileService;
    private final ProductDao productDao;
    private final WarehouseService warehouseService;
    private final OrderReturnService returnService;

    @Override
    public SingleResult<Map<String, Object>> addInit(Long userId, Long tenantId, String source, Long whId) {
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("cellList", storageLocationDao.selectList(
                Wrappers.<StorageLocationDo>lambdaQuery()
                        .eq(StorageLocationDo::getTenantId, tenantId)
                        .eq(StorageLocationDo::getWhId, whId)
                        .orderByDesc(StorageLocationDo::getPickingLocationSort)
        ));
        map.put("companyList", companyService.getCompanyList(userId, tenantId, source));
        return SingleResult.success(map);
    }

    @Override
    public void add(ClaimAddBo req, Long userId) {
        ClaimDo one = this.lambdaQuery()
                .eq(ClaimDo::getTrackingNumber, req.getTrackingNumber())
                .ge(ClaimDo::getCreateTime, DateUtil.offset(DateUtil.date(), DateField.MONTH, -3))
                .last("limit 1")
                .one();
        if (Objects.nonNull(one)) {
            throw new BizException(SysConstant.TrackingNumber_Correspond_Claim_Exists, req.getTrackingNumber());
        }
//        ClaimDo entityDo = new ClaimDo();
//        BeanUtils.copyProperties(req, entityDo);
        ClaimDo entityDo = BeanConvert.INSTANCE.claimAddBoToClaimDo(req);
        entityDo.setStatus(ClaimStatusEnum.zero.getValue());
        entityDo.setClaimNo(this.genClaimNo(entityDo.getOperateType()));
        entityDo.setExpireTime(DateUtil.parse(req.getExpireTime(), "yyyy-MM-dd"));
        if (StrUtil.isNotEmpty(req.getFileIds())) {
            fileService.uploadFileMult(req.getFileIds().split(","));
        }
        baseMapper.insert(entityDo);
        if (CollUtil.isNotEmpty(req.getProducts())) {
            List<ClaimProductDo> claimProductDoList = JSONUtil.toList(
                    JSONUtil.toJsonStr(req.getProducts()),
                    ClaimProductDo.class
            );
            claimProductDoList.forEach(claimProductDo -> claimProductDo.setClaimId(entityDo.getId()));
            claimProductService.saveBatch(claimProductDoList);
        }
    }

    /**
     * 生成认领单号
     */
    private String genClaimNo(Integer operateType) {
        // 每周重置认领单序号
        //生成入库单号
        String claimNoSeq = String.valueOf(redisTemplate.opsForValue().increment(RedisKeyConstants.CLAIM_NO_SEQ));
        //序列号不足六位在前面补0
        if (claimNoSeq.length() < 6) {
            claimNoSeq = "000000".substring(0, 6 - claimNoSeq.length()) + claimNoSeq;
        }
        return "IC"
                + operateType
                + DateUtil.format(DateUtil.date(), "yyMMdd")
                + claimNoSeq;
    }

    @Override
    public int delete(Long id, Long userId) {
        return baseMapper.deleteById(id);
    }

    @Override
    public ClaimDetailVo detail(Long id) {
        ClaimDo entityDo = baseMapper.selectById(id);
//        ClaimDetailVo res = new ClaimDetailVo();
//        BeanUtils.copyProperties(entityDo, res);
        ClaimDetailVo res = BeanConvert.INSTANCE.claimDoToClaimDetailVo(entityDo);
        Long cellId = entityDo.getTemporaryCell();
        if (Objects.nonNull(cellId)) {
            StorageLocationDo storageLocationDo = storageLocationDao.selectById(cellId);
            if (Objects.nonNull(storageLocationDo)) {
                res.setCellCode(storageLocationDo.getCode());
            }
        }
        String fileIds = entityDo.getFileIds();
        if (StringUtils.isNotEmpty(fileIds)) {
            res.setFileUrls(fileService.getFilePathMulti(fileIds));
        }
        List<ClaimProductDo> claimProductDoList = claimProductService.lambdaQuery()
                .eq(ClaimProductDo::getClaimId, id)
                .list();
        if (CollUtil.isNotEmpty(claimProductDoList)) {
            List<ClaimProductVo> productVos = JSONUtil.toList(JSONUtil.toJsonStr(claimProductDoList), ClaimProductVo.class);
            Map<Long, String> productIdMatchDo = productDao.selectList(
                    Wrappers.<ProductDo>lambdaQuery()
                            .in(
                                    ProductDo::getId,
                                    productVos.stream()
                                            .map(ClaimProductVo::getProductId)
                                            .collect(Collectors.toList())
                            )
            ).stream().collect(Collectors.toMap(ProductDo::getId, ProductDo::getProductName));
            productVos.forEach(productVo -> productVo.setProductName(productIdMatchDo.get(productVo.getProductId())));
            res.setProductVos(productVos);
        }
        return res;
    }

    @Override
    public SingleResult<Map<String, Object>> pageInit(Long userId, Long tenantId, Long companyId, String source) {
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("statusList", ClaimStatusEnum.getClaimStatusList());
        if ("oms".equals(source)) {
            map.put("warehouseList", warehouseService.getWarehouseCompany(companyId, tenantId));
        } else {
            map.put("companyList", companyService.getCompanyList(userId, tenantId, source));
            map.put("warehouseList", warehouseService.getList(tenantId));
        }
        return SingleResult.success(map);
    }

    @Override
    public PageResult<ClaimVo> getListPage(ClaimQueryBo bo) {
        IPage<ClaimDo> page = new Page<>(bo.getCurrent(), bo.getPageSize());
        page = this.page(page, this.queryBuild(bo));
        List<ClaimVo> resList = this.handlerListAfter(page.getRecords());
        return new PageResult<>(resList, (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
    }

    @Override
    public void cancel(Long id) {
        this.lambdaUpdate()
                .eq(ClaimDo::getId, id)
                .set(ClaimDo::getStatus, ClaimStatusEnum.two.getValue())
                .set(ClaimDo::getCancelTime, DateUtil.date())
                .update();
    }

    @Override
    public void claimOperate(ClaimOperate bo, Long claimCompanyId) {
        this.lambdaUpdate()
                .eq(ClaimDo::getId, bo.getId())
                .set(ClaimDo::getClaimType, bo.getClaimType())
                .set(ClaimDo::getLinkNo, bo.getLinkNo())
                .set(ClaimDo::getStatus, ClaimStatusEnum.one.getValue())
                .set(ClaimDo::getClaimTime, DateUtil.date())
                .set(ClaimDo::getClaimCompanyId, claimCompanyId)
                .update();
    }

    @Override
    public SingleResult<Map<Integer, Integer>> getListPageStatusStatistics(ClaimQueryBo bo) {
        List<ClaimDo> list = this.list(this.queryBuild(bo));
        //根据状态分组统计
        Map<Integer, List<ClaimDo>> statusMap = list.stream()
                .collect(Collectors.groupingBy(ClaimDo::getStatus));
        Map<Integer, Integer> map = new HashMap<>(8);
        for (ClaimStatusEnum statusEnum : ClaimStatusEnum.values()) {
            map.put(statusEnum.getValue(), statusMap.getOrDefault(statusEnum.getValue(), Collections.emptyList()).size());
        }
        return SingleResult.success(map);
    }

    @Override
    public void exportWms(ClaimQueryBo bo, HttpServletResponse response) throws IOException {
        List<ClaimDo> claimDoList = this.list(this.queryBuild(bo));
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        Map<Long, List<ClaimProductDo>> idMatchProductList = claimProductService.lambdaQuery()
                .in(ClaimProductDo::getClaimId, claimDoList.stream().map(ClaimDo::getId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.groupingBy(ClaimProductDo::getClaimId));
        Set<Long> cellIdList = claimDoList.stream().map(ClaimDo::getTemporaryCell).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, String> cellIdMatchCode = new HashMap<>();
        if (CollUtil.isNotEmpty(cellIdList)) {
            cellIdMatchCode = storageLocationDao.selectList(
                    Wrappers.<StorageLocationDo>lambdaQuery()
                            .in(StorageLocationDo::getId, cellIdList)
            ).stream().collect(Collectors.toMap(StorageLocationDo::getId, StorageLocationDo::getCode));
        }
        Set<Long> companyIdSet = new HashSet<>();
        Set<Long> companyIdList = claimDoList.stream().map(ClaimDo::getCompanyId).filter(Objects::nonNull).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(companyIdList)) {
            companyIdSet.addAll(companyIdList);
        }
        Set<Long> claimCompanyIdList = claimDoList.stream().map(ClaimDo::getClaimCompanyId).filter(Objects::nonNull).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(claimCompanyIdList)) {
            companyIdList.addAll(claimCompanyIdList);
        }
        Map<Long, CompanyDo> companyIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(companyIdSet)) {
            companyIdMatch = companyService.lambdaQuery()
                    .in(CompanyDo::getId, companyIdSet)
                    .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        }
        List<ClaimWmsDto> dtoList = new ArrayList<>();
        for (ClaimDo claimDo : claimDoList) {
//            ClaimWmsDto dto = new ClaimWmsDto();
//            BeanUtils.copyProperties(claimDo, dto);
            ClaimWmsDto dto = BeanConvert.INSTANCE.claimDoToClaimWmsDto(claimDo);
            if (Objects.nonNull(claimDo.getCompanyId())) {
                CompanyDo companyDo = companyIdMatch.get(claimDo.getCompanyId());
                if (companyDo != null) {
                    dto.setDesignatedCustomer(companyDo.getName());
                }
            }
            if (Objects.nonNull(claimDo.getClaimCompanyId())) {
                CompanyDo companyDo = companyIdMatch.get(claimDo.getClaimCompanyId());
                if (companyDo != null) {
                    dto.setClaimedCustomer(companyDo.getName());
                }
            }
            if (Objects.nonNull(claimDo.getTemporaryCell())) {
                dto.setTemporaryCell(cellIdMatchCode.get(claimDo.getTemporaryCell()));
            }
            dto.setStatus(ClaimStatusEnum.getNameByValue(claimDo.getStatus()));
            List<ClaimProductDo> claimProductDoList = idMatchProductList.get(claimDo.getId());
            if (CollUtil.isNotEmpty(claimProductDoList)) {
                for (ClaimProductDo claimProductDo : claimProductDoList) {
                    ClaimWmsDto dtoTemp = JSONUtil.toBean(JSONUtil.toJsonStr(dto), ClaimWmsDto.class);
                    dtoTemp.setSku(claimProductDo.getSku());
                    dtoTemp.setFnsku(claimProductDo.getFnSku());
                    dtoTemp.setQuantity(claimProductDo.getGoodQty() + claimProductDo.getBadQty());
                    dtoList.add(dtoTemp);
                }
            } else {
                dtoList.add(dto);
            }
        }
        ExcelUtils.exportExcel(dtoList, ClaimWmsDto.class, "InboundClaim_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response,"xls");
    }

    @Override
    public void exportOms(ClaimQueryBo bo, HttpServletResponse response) throws IOException {
        List<ClaimDo> claimDoList = this.list(this.queryBuild(bo));
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, claimDoList.stream().map(ClaimDo::getWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, List<ClaimProductDo>> idMatchProductList = claimProductService.lambdaQuery()
                .in(ClaimProductDo::getClaimId, claimDoList.stream().map(ClaimDo::getId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.groupingBy(ClaimProductDo::getClaimId));
        Map<Long, ProductDo> productIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(idMatchProductList)) {
            List<Long> productIdList = idMatchProductList.values().stream().flatMap(List::stream).map(ClaimProductDo::getProductId).collect(Collectors.toList());
            productIdMatch = productDao.selectBatchIds(productIdList).stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        List<ClaimOmsDto> dtoList = new ArrayList<>();
        for (ClaimDo claimDo : claimDoList) {
//            ClaimOmsDto dto = new ClaimOmsDto();
//            BeanUtils.copyProperties(claimDo, dto);
            ClaimOmsDto dto = BeanConvert.INSTANCE.claimDoToClaimOmsDto(claimDo);
            WarehouseDo whDo = whIdMatch.get(claimDo.getWhId());
            if (whDo != null) {
                dto.setWarehouseNameCode(whDo.getWhNameCn() + "(" + whDo.getWhCode() + ")");
            } else {
                dto.setWarehouseNameCode("");
            }
            dto.setStatus(ClaimStatusEnum.getNameByValue(claimDo.getStatus()));
            if (Objects.isNull(claimDo.getCompanyId())) {
                if (claimDo.getTrackingNumber().length() >= 4) {
                    dto.setTrackingNumber(claimDo.getTrackingNumber().substring(0, claimDo.getTrackingNumber().length() - 4) + "****");
                } else {
                    dto.setTrackingNumber("****");
                }
            }
            List<ClaimProductDo> claimProductDoList = idMatchProductList.get(claimDo.getId());
            if (CollUtil.isNotEmpty(claimProductDoList)) {
                for (ClaimProductDo claimProductDo : claimProductDoList) {
                    ClaimOmsDto dtoTemp = JSONUtil.toBean(JSONUtil.toJsonStr(dto), ClaimOmsDto.class);
                    dtoTemp.setSku(claimProductDo.getSku());
                    dtoTemp.setFnsku(claimProductDo.getFnSku());
                    dtoTemp.setProductName(productIdMatch.get(claimProductDo.getProductId()).getProductName());
                    dtoTemp.setGoodNumber(claimProductDo.getGoodQty());
                    dtoTemp.setBadNumber(claimProductDo.getBadQty());
                    dtoList.add(dtoTemp);
                }
            } else {
                dtoList.add(dto);
            }
        }
        ExcelUtils.exportExcel(dtoList, ClaimOmsDto.class, "InboundClaim_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response,"xls");
    }

    /**
     * 查询分页列表之后对列表数据的处理
     */
    private List<ClaimVo> handlerListAfter(List<ClaimDo> list) {
        List<ClaimVo> result = JSONUtil.toList(JSONUtil.toJsonStr(list), ClaimVo.class);
        List<Long> cellIdList = result.stream()
                .map(ClaimVo::getTemporaryCell)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        Map<Long, String> cellIdMatchCode = new HashMap<>();
        if (CollUtil.isNotEmpty(cellIdList)) {
            cellIdMatchCode = storageLocationDao.selectList(
                    Wrappers.<StorageLocationDo>lambdaQuery()
                            .in(StorageLocationDo::getId, cellIdList)
            ).stream().collect(Collectors.toMap(StorageLocationDo::getId, StorageLocationDo::getCode));
        }
        List<String> returnNoList = result.stream().filter(i -> Objects.nonNull(i.getClaimType()) && i.getClaimType().equals(1)).map(ClaimVo::getLinkNo).collect(Collectors.toList());
        Map<String, OrderReturnDo> returnNoMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(returnNoList)) {
            returnNoMatch = returnService.lambdaQuery()
                    .in(OrderReturnDo::getReturnNo, returnNoList)
                    .list().stream().collect(Collectors.toMap(OrderReturnDo::getReturnNo, val -> val));
        }
        for (ClaimVo claimVo : result) {
            claimVo.setFileUrls(fileService.getFilePathMulti(claimVo.getFileIds()));
            if (Objects.isNull(claimVo.getCompanyId())) {
                if (claimVo.getTrackingNumber().length() >= 4) {
                    claimVo.setExpressNo(claimVo.getTrackingNumber().substring(0, claimVo.getTrackingNumber().length() - 4) + "****");
                } else {
                    claimVo.setTrackingNumber("****");
                }
            }
            claimVo.setCellCode(cellIdMatchCode.getOrDefault(claimVo.getTemporaryCell(), null));
            if (Objects.nonNull(claimVo.getClaimType()) && claimVo.getClaimType().equals(1) && StrUtil.isNotEmpty(claimVo.getLinkNo())) {
                OrderReturnDo returnDo = returnNoMatch.get(claimVo.getLinkNo());
                if (Objects.nonNull(returnDo)) {
                    claimVo.setLinkId(returnDo.getId());
                }
            }
        }
        return result;
    }

    private LambdaQueryWrapper<ClaimDo> queryBuild(ClaimQueryBo bo) {
        LambdaQueryWrapper<ClaimDo> lqw = new LambdaQueryWrapper<>();
        lqw.in(CollUtil.isNotEmpty(bo.getIds()), ClaimDo::getId, bo.getIds());
        lqw.in(CollUtil.isNotEmpty(bo.getCompanyIds()), ClaimDo::getCompanyId, bo.getCompanyIds());
        if (Objects.nonNull(bo.getCompanyId())) {
            lqw.and(i ->
                    i.eq(ClaimDo::getCompanyId, bo.getCompanyId())
                            .or(k -> k.eq(ClaimDo::getClaimCompanyId, bo.getCompanyId()))
                            .or(k -> k.isNull(ClaimDo::getCompanyId).isNull(ClaimDo::getClaimCompanyId))
            );
        }
        lqw.ge(
                StringUtils.isNotBlank(bo.getTimeType()) && "createTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getStartDate()),
                ClaimDo::getCreateTime,
                bo.getStartDate()
        );
        lqw.le(
                StringUtils.isNotBlank(bo.getTimeType()) && "createTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getEndDate()),
                ClaimDo::getCreateTime,
                bo.getEndDate()
        );
        lqw.ge(
                StringUtils.isNotBlank(bo.getTimeType()) && "claimTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getStartDate()),
                ClaimDo::getClaimTime,
                bo.getStartDate()
        );
        lqw.le(
                StringUtils.isNotBlank(bo.getTimeType()) && "claimTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getEndDate()),
                ClaimDo::getClaimTime,
                bo.getEndDate()
        );
        lqw.ge(
                StringUtils.isNotBlank(bo.getTimeType()) && "cancelTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getStartDate()),
                ClaimDo::getCancelTime,
                bo.getStartDate()
        );
        lqw.le(
                StringUtils.isNotBlank(bo.getTimeType()) && "cancelTime".equals(bo.getTimeType()) && Objects.nonNull(bo.getEndDate()),
                ClaimDo::getCancelTime,
                bo.getEndDate()
        );
        lqw.in(CollUtil.isNotEmpty(bo.getClaimNoList()), ClaimDo::getClaimNo, bo.getClaimNoList());
        lqw.in(CollUtil.isNotEmpty(bo.getTrackingNumberList()), ClaimDo::getTrackingNumber, bo.getTrackingNumberList());
        lqw.eq(Objects.nonNull(bo.getStatus()), ClaimDo::getStatus, bo.getStatus());
        lqw.eq(Objects.nonNull(bo.getWhId()), ClaimDo::getWhId, bo.getWhId());
        lqw.in(CollUtil.isNotEmpty(bo.getWhIds()), ClaimDo::getWhId, bo.getWhIds());
        lqw.eq(Objects.nonNull(bo.getTenantId()), ClaimDo::getTenantId, bo.getTenantId());
        if (StrUtil.isNotBlank(bo.getSortName())) {
            lqw.last("order by " + bo.getSortName() + " " + bo.getSortOrder() + ",id " + " " + bo.getSortOrder());
        } else {
            lqw.orderByDesc(ClaimDo::getCreateTime, ClaimDo::getId);
        }
        return lqw;
    }
}
